package com.example.teaching_server.service;

import com.example.teaching_server.dto.WeekDTO;
import com.example.teaching_server.entity.TermEntity;
import com.example.teaching_server.exception.TermNotFoundException;
import com.example.teaching_server.repository.TermRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAdjusters;
import java.util.ArrayList;
import java.util.List;

/**
 * 周次计算服务
 */
@Slf4j
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class WeekCalculationService {

    private final TermRepository termRepository;

    /**
     * 获取学期所有周次信息
     */
    public List<WeekDTO> getTermWeeks(Long termId, int startDayOfWeek) {
        log.debug("获取学期周次信息: termId={}, startDayOfWeek={}", termId, startDayOfWeek);

        validateTermId(termId);
        validateStartDayOfWeek(startDayOfWeek);

        TermEntity term = getTermById(termId);
        DayOfWeek firstDayOfWeek = DayOfWeek.of(startDayOfWeek);

        return calculateTermWeeks(term, firstDayOfWeek);
    }

    /**
     * 获取当前学期周次信息
     */
    public List<WeekDTO> getCurrentTermWeeks(int startDayOfWeek) {
        log.debug("获取当前学期周次信息: startDayOfWeek={}", startDayOfWeek);

        validateStartDayOfWeek(startDayOfWeek);

        TermEntity currentTerm = termRepository.findByIsCurrentTrue()
                .orElseThrow(() -> {
                    log.warn("未找到当前学期");
                    return new TermNotFoundException("当前未设置活动学期");
                });

        DayOfWeek firstDayOfWeek = DayOfWeek.of(startDayOfWeek);
        return calculateTermWeeks(currentTerm, firstDayOfWeek);
    }

    /**
     * 获取指定日期所在的周信息
     */
    public WeekDTO getWeekByDate(LocalDate date, int startDayOfWeek) {
        log.debug("获取指定日期周信息: date={}, startDayOfWeek={}", date, startDayOfWeek);

        validateDate(date);
        validateStartDayOfWeek(startDayOfWeek);

        DayOfWeek firstDayOfWeek = DayOfWeek.of(startDayOfWeek);
        LocalDate weekStart = getWeekStart(date, firstDayOfWeek);
        LocalDate weekEnd = getWeekEnd(date, firstDayOfWeek);

        // 计算周数（基于当前年份）
        int weekNumber = calculateWeekNumber(weekStart);

        return createWeekDTO(weekNumber, weekStart, weekEnd);
    }

    /**
     * 计算学期周次
     */
    private List<WeekDTO> calculateTermWeeks(TermEntity term, DayOfWeek firstDayOfWeek) {
        LocalDate currentDate = term.getStartDate();
        LocalDate endDate = term.getEndDate();
        List<WeekDTO> weeks = new ArrayList<>();
        int weekCounter = 1;

        while (!currentDate.isAfter(endDate)) {
            LocalDate weekStart = getWeekStart(currentDate, firstDayOfWeek);
            LocalDate weekEnd = getWeekEnd(currentDate, firstDayOfWeek);

            // 确保结束日期不超过学期结束日期
            if (weekEnd.isAfter(endDate)) {
                weekEnd = endDate;
            }

            WeekDTO week = createWeekDTO(weekCounter, weekStart, weekEnd);
            weeks.add(week);

            // 跳到下一周
            currentDate = weekEnd.plusDays(1);
            weekCounter++;
        }

        log.debug("计算完成: 学期{}共有{}周", term.getTermName(), weeks.size());
        return weeks;
    }

    /**
     * 创建周DTO
     */
    private WeekDTO createWeekDTO(int weekNumber, LocalDate startDate, LocalDate endDate) {
        WeekDTO week = new WeekDTO();
        week.setWeekNumber(weekNumber);
        week.setStartDate(startDate);
        week.setEndDate(endDate);
        week.setDisplayName(generateDisplayName(week));
        return week;
    }

    /**
     * 获取周开始日期
     */
    private LocalDate getWeekStart(LocalDate date, DayOfWeek firstDayOfWeek) {
        return date.with(TemporalAdjusters.previousOrSame(firstDayOfWeek));
    }

    /**
     * 获取周结束日期
     */
    private LocalDate getWeekEnd(LocalDate date, DayOfWeek firstDayOfWeek) {
        return date.with(TemporalAdjusters.nextOrSame(firstDayOfWeek.plus(6)));
    }

    /**
     * 生成显示名称
     */
    private String generateDisplayName(WeekDTO week) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("M月d日");
        return String.format("第%d周 (%s-%s)",
                week.getWeekNumber(),
                week.getStartDate().format(formatter),
                week.getEndDate().format(formatter));
    }

    /**
     * 计算周数（基于年份）
     */
    private int calculateWeekNumber(LocalDate date) {
        // 简单的基于日期的周数计算，可根据需要调整为学期相关的周数
        return (int) java.time.temporal.WeekFields.of(DayOfWeek.MONDAY, 1)
                .weekOfYear()
                .getFrom(date);
    }

    /**
     * 根据ID获取学期
     */
    private TermEntity getTermById(Long termId) {
        return termRepository.findById(termId)
                .orElseThrow(() -> {
                    log.warn("学期不存在: termId={}", termId);
                    return new TermNotFoundException("学期不存在");
                });
    }

    /**
     * 验证学期ID
     */
    private void validateTermId(Long termId) {
        if (termId == null || termId <= 0) {
            throw new IllegalArgumentException("学期ID无效");
        }
    }

    /**
     * 验证周起始日
     */
    private void validateStartDayOfWeek(int startDayOfWeek) {
        if (startDayOfWeek < 1 || startDayOfWeek > 7) {
            throw new IllegalArgumentException("周起始日必须在1-7之间");
        }
    }

    /**
     * 验证日期
     */
    private void validateDate(LocalDate date) {
        if (date == null) {
            throw new IllegalArgumentException("日期不能为空");
        }
    }

    /**
     * 获取当前周信息
     */
    public WeekDTO getCurrentWeek(int startDayOfWeek) {
        log.debug("获取当前周信息: startDayOfWeek={}", startDayOfWeek);

        validateStartDayOfWeek(startDayOfWeek);

        LocalDate today = LocalDate.now();
        return getWeekByDate(today, startDayOfWeek);
    }

    /**
     * 检查日期是否在学期范围内
     */
    public boolean isDateInTerm(Long termId, LocalDate date) {
        log.debug("检查日期是否在学期内: termId={}, date={}", termId, date);

        validateTermId(termId);
        validateDate(date);

        try {
            TermEntity term = getTermById(termId);
            return !date.isBefore(term.getStartDate()) && !date.isAfter(term.getEndDate());
        } catch (Exception e) {
            log.warn("检查学期日期范围失败: termId={}, date={}", termId, date, e);
            return false;
        }
    }

    /**
     * 获取学期的总周数
     */
    public int getTermWeekCount(Long termId, int startDayOfWeek) {
        log.debug("获取学期总周数: termId={}, startDayOfWeek={}", termId, startDayOfWeek);

        List<WeekDTO> weeks = getTermWeeks(termId, startDayOfWeek);
        return weeks.size();
    }
}
